/*
 * Utils <http://legocode.googlecode.com/svn/trunk/javascript/utils.js>
 * Copyright (c) 2010 MindstormsKid <mindstormskd@gmail.com>
 * Licensed under the MIT license <http://www.opensource.org/licenses/mit-license.php>
 *
 * Some general extensions for use in Javascript 1.8+ enabled environments
 */
(function(){
	let $A = Array.prototype, $N = Number.prototype, $S = String.prototype, $D = Date.prototype;
	
	
	/*** Objects ***/
	Object.each = function(o, cb, thisobj){
		thisobj = thisobj || o;
		for (let [k, v] in Iterator(o)) {
			if (o.hasOwnProperty(k) && cb.call(thisobj, v, k, o) === false) { break; }
		}
		return o;
	};
	
	Object.merge = function(){
		let args = Array.slice.call(arguments), target = args.shift();
		
		args.forEach(function(o){
			Object.each(o, function(v, k){
				if (typeof v === "object" && target.hasOwnProperty(k)) {
					target[k] = Object.merge(target[k], v);
				} else if (typeof v !== "undefined") {
					target[k] = v;
				}
			});
		});
		
		return target;
	};
	
	Object.clone = function(o){
		return Object.merge({}, o);
	};
	
	Object.keys = function(o){
		return [k for (k in o)];
	};
	Object.vals = function(){
		return [v for each (v in o)];
	};
	
	["Array", "Date", "Function", "Number", "RegExp", "String", "Object"].forEach(function(v){
		Object["is" + v] = function(o){
			return Object.prototype.toString.call(o) === "[object " + v + "]";
		};
	});
	Object.isElement = function(){
		return !!(object && object.nodeType === 1);
	};
	
	
	/*** Arrays ***/
	$A.each = function(cb, thisobj){
		this.forEach(cb, thisobj || this);
		return this;
	};
	
	$A.__defineGetter__("first", function(){
		return this[0];
	});
	$A.__defineGetter__("last", function(){
		return this[this.length - 1];
	});
	
	$A.clean = function(){
		return this.filter(function(o){ return o != null; });
	};
	
	$A.associate = function(k){
		let o = {};
		for (let i = 0, a = this, k = k, len = Math.min(this.length, k.length); i < len; i++) {
			o[k[i]] = a[i];
		}
		return obj;
	};
	
	$A.contains = function(s, p){
		return this.indexOf(s, p) !== -1;
	};
	
	$A.random = function(){
		return this[Math.floor(Math.random() * this.length)];
	};
	
	$A.erase = function(o){
		for (let i = this.length; i--;) {
			this[i] === item && this.splice(i, 1);
		}
		return this;
	};
	
	$A.empty = function(){
		this.length = 0;
		return this;
	};
	
	$A.flatten = function(){
		let a = [];
		for (let a = this, len = this.length, i = 0; i < len; i++) {
			let v = a[i];
			a = a.concat(Object.isArray(v) ? v.flatten() : v);
		}
		return a;
	};
	
	// Array Remove - By John Resig (MIT Licensed)
	$A.remove = function(from, to) {
		let rest = this.slice((to || from) + 1 || this.length);
		this.length = from < 0 ? this.length + from : from;
		return this.push.apply(this, rest);
	};
	
	
	/*** Strings ***/
	$S.toArray = function(){
		return this.split("");
	};
	
	$S.isBlank = function(){
		return !this.trim();
	};
	
	$S.camelize = function(c){
		return this.replace(new RegExp((c || "-") + "+(.)?", "g"), function(m, c){
			return c ? c.toUpperCase() : "";
		});
	};
	$S.decamelize = function(c){
		return this.replace(/([A-Z])/g, (c || "-") + "$1").toLowerCase();
	};
	
	$S.template = function(o, s, e){
		return this.replace(new RegExp("(?:\\\\)?" + (s || "\\{\\$") + "(.+?)" + (e || "\\}"), "g"), function(m, k){
			let v = o[k];
			return m[0] === "\\" ? m.substr(1) : (Object.isFunction(v) ? v() : v); // {$blah} -> <something>, \{$blah} -> {$blah}
		});
	};
	
	$S.sprintf = function(){
		let args = Array.slice.call(arguments), i = 0;
		return this.replace(/%([a-z]|%)/g, function(m, f){
			let v = args[i++];
			if (f === "s") {
				return v;
			} else if (f === "f") {
				return parseFloat(v);
			} else if (f === "i") {
				return parseInt(v, 10);
			} else if (f === "n") {
				return (v + "").replace(/^(\d+?)?(e\d+|\.\d+)?$/i, function(m, n, $2){
					return n.reverse().replace(/(\d\d\d)(?!$)/g, "$1,").reverse() + $2;
				});
			} else if (f === "%") {
				return "%%";
			}
		});
	};
	
	$S.reverse = function(){
		return this.split("").reverse().join("");
	};
	
	
	/*** Numbers ***/
	$N.abs = function(){
		return Math.abs(this);
	};
	
	$N.round = function(n){
		return n ? this.toFixed(n) / 1 : Math.round(this);
	};
	["ceil", "floor"].forEach(function(k){
		$N[k] = function(n){
			let p = Math.pow(10, n);
			return n ? (this * p)[k]() / p : Math[k](this);
		};
	});
	
	$N.pow = function(exp) {
		return Math.pow(this, exp);
	};
	
	
	/*** Math ***/
	Math.rand = function(m){
		return Math.floor(Math.random() * (m + 1));
	};
	
	
	/*** etc. ***/
	JSON.encode = JSON.stringify;
	JSON.decode = JSON.parse;
	
	$A.toJSON = $D.toJSON = Object.toJSON = function(){
		return JSON.encode(this);
	};
})();